package com.realife.eson.common.util.network;


import com.realife.eson.common.asserts.AssertNP;
import com.realife.eson.common.constant.HttpConstant;
import com.realife.eson.common.util.UniqueCodeUtil;
import com.realife.eson.common.util.common.ResourcesCloseUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.AbstractHttpMessage;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;


/**
 * @version V1.0
 * @author: realife_cheng
 * @date: 2019/12/13 23:55
 * @description: Http请求封装
 */
@Slf4j
@Component
@SuppressWarnings("all")
public class HttpUtils {
    public static final int connTimeout = 10000;
    public static final int readTimeout = 10000;
    public static final String charset = "UTF-8";
    private static HttpClient client = null;
    public final static Map<String, String> localCache = new HashMap<>();


    @Autowired
    private PoolingHttpClientConnectionManager cm;


    private static List<String> userAgentList = null;

    static {
        userAgentList = new ArrayList<String>();
        userAgentList.add("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36");
        userAgentList.add("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:73.0) Gecko/20100101 Firefox/73.0");
        userAgentList.add("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.5 Safari/605.1.15");
        userAgentList.add("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299");
        userAgentList.add("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36");
        userAgentList.add("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0");
        userAgentList.add("Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36");
        userAgentList.add("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36");
        userAgentList.add("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:93.0) Gecko/20100101 Firefox/93.0");
        userAgentList.add("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36 Edg/95.0.1020.53");
        userAgentList.add("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36");

        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(128);
        cm.setDefaultMaxPerRoute(128);
        client = HttpClients.custom().setConnectionManager(cm).build();
    }


    /**
     * 根据请求地址下载数据
     *
     * @param url
     * @return 页面数据
     */
    public String doGetHtml(String url) {
        //获取HttpClient对象
        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(this.cm).build();
        //创建httpGet请求对象，设置URL
        HttpGet httpGet = new HttpGet(url);
        //伪装成浏览器的请求
        httpGet.setHeader("User-Agent", userAgentList.get(new Random().nextInt(userAgentList.size())));
        httpGet.setHeader("cookie", "SINAGLOBAL=2249913306969.0425.1630045160499; SUBP=0033WrSXqPxfM725Ws9jqgMF55529P9D9WFzqB4IK_Wq8y-sqDkUSk-M5JpX5KMhUgL.Fozpeo.Neo2Ne052dJLoIEBLxK-LBo.LBonLxKMLBKqL12qLxKqLB-BLBKBLxKqL1KMLBoqt; ALF=1669461695; SSOLoginState=1637925695; SCF=AvBj9AktZqPlXaBr6FqyswB90uLRfm4isGl7_pUjoW94VtrtiYAWYVy3wXTAvzFTalggQLRjwUZQ5HkuTJ7fbZQ.; SUB=_2A25MpLNvDeRhGeRP6VsW8i_LyDyIHXVv06OnrDV8PUNbmtANLWfVkW9NUFT7NQwfmh2d4zYvYWehML44eOWm53CP; _s_tentry=login.sina.com.cn; UOR=www.bilibili.com,service.weibo.com,www.bilibili.com; Apache=4856189838107.532.1637925697438; ULV=1637925697445:35:11:3:4856189838107.532.1637925697438:1637755543251; XSRF-TOKEN=HlJhlgk4J0uqsq84BTyO94Z3; WBPSESS=8WKsNKamsXTRCXZQoHWj8BRo3fvLuicxdTTa4YF-MV-olZ5dGJYoJ1FTya1MAsaKMr2l8C6EQwZA6m99cXLht4bodPGPDAoJN9GzXnsHc_9CkaeG5v4Fdhdu18VXZGJGbJl8Ra20W9o7Vn_juxRf-Q==");
        //httpGet.setHeader("", "");
        //配置请求
        httpGet.setConfig(this.getConfig());
        CloseableHttpResponse response = null;
        try {
            //使用HTTPClient发起请求，获取响应
            response = httpClient.execute(httpGet);
            //解析响应，返回结果
            if (response.getStatusLine().getStatusCode() == 200) {
                //判断响应体Entity是否不为空，如果不为空就可以使用entityUtils
                if (null != response.getEntity()) {
                    String content = EntityUtils.toString(response.getEntity(), "utf8");
                    return content;
                }
            }
        } catch (Exception e) {
            log.info("[**网页数据获取失败,{}**]", e.getMessage());
        } finally {
            //释放资源
            ResourcesCloseUtils.close(response);
        }
        return "null";
    }


    /**
     * 下载图片
     *
     * @param url
     * @return 图片名称
     */
    public String doGetImage(String url, String dstPath) {
        //获取HttpClient对象
        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(this.cm).build();
        //创建httpGet请求对象，设置URL
        HttpGet httpGet = new HttpGet(url);
        //伪装成浏览器的请求
        httpGet.setHeader("User-Agent", userAgentList.get(new Random().nextInt(userAgentList.size())));
        //设置请求信息
        httpGet.setConfig(this.getConfig());
        CloseableHttpResponse response = null;
        try {
            //使用HTTPClient发起请求，获取响应
            response = httpClient.execute(httpGet);
            //解析响应，返回结果
            if (response.getStatusLine().getStatusCode() == 200) {
                //判断响应体Entity是否不为空，如果不为空就可以使用entityUtils
                if (null != response.getEntity()) {
                    //下载图片
                    //获取图片后缀 .jpg
                    String extName = url.substring(url.lastIndexOf("."));
                    //创建图片名
                    String picName = UniqueCodeUtil.uniqueCode(url) + extName;
                    //下载图片
                    OutputStream outputStream = new FileOutputStream(new File(dstPath + picName));
                    response.getEntity().writeTo(outputStream);
                    //返回图片名称
                    return picName;
                }
            }
        } catch (Exception e) {
            log.info("[**======> {} **]", e.getMessage() + " >> " + e.getStackTrace()[0] + " >> " + e.getCause());
        } finally {
            //释放资源
            ResourcesCloseUtils.close(response);
        }
        return "null";
    }


    /**
     * get douyin video
     *
     * @param url
     * @return
     */
    public void doGetVideo(String url, String dstPath) {
        //获取HttpClient对象
        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(this.cm).build();
        //创建名
        String picName = UniqueCodeUtil.uniqueCode(url) + ".mp4";
        //创建httpGet请求对象，设置URL
        HttpGet httpGet = new HttpGet(url);
        //伪装成浏览器的请求
        httpGet.setHeader("User-Agent", userAgentList.get(new Random().nextInt(userAgentList.size())));
        //设置请求信息
        httpGet.setConfig(this.getConfig());
        CloseableHttpResponse response1 = null;
        try {
            //使用HTTPClient发起请求，获取响应
            response1 = httpClient.execute(httpGet);
            int statusCode = response1.getStatusLine().getStatusCode();
            //解析响应，返回结果
            if (statusCode == 200) {
                //判断响应体Entity是否不为空，如果不为空就可以使用entityUtils
                if (null != response1.getEntity()) {
                    OutputStream outputStream = new FileOutputStream(new File(dstPath + picName));
                    response1.getEntity().writeTo(outputStream);
                }
            }
        } catch (Exception e) {
            log.info("[**======> {} **]", e.getMessage() + " >> " + e.getStackTrace()[0] + " >> " + e.getCause());
        } finally {
            ResourcesCloseUtils.close(response1);
        }
    }


    /**
     * 设置请求信息
     *
     * @return
     */
    private RequestConfig getConfig() {
        return RequestConfig.custom()
                .setConnectTimeout(1000000)
                .setConnectionRequestTimeout(5000000)
                .setSocketTimeout(1000000)
                .build();
    }


    /**
     * ===================================================== HttpUtil ===============================================
     * ==============================================================================================================
     */
    static final String POST = "POST";
    static final String GET = "GET";
    /**
     * ms
     */
    static final int CONN_TIMEOUT = 30000;
    static final int READ_TIMEOUT = 30000;


    /**
     * post 方式发送http请求.
     * TODO 不能满足所有的请求, 怎么传body? 怎么传form?
     *
     * @param strUrl
     * @param reqData
     * @return
     */
    public static byte[] doPost(String strUrl, byte[] reqData) {
        return send(strUrl, POST, reqData);
    }

    /**
     * get方式发送http请求.
     *
     * @param strUrl
     * @return
     */
    public static byte[] doGet(String strUrl) {
        return send(strUrl, GET, null);
    }


    /**
     * @param strUrl    地址
     * @param reqMethod 请求方式
     * @param reqData   请求参数
     * @return
     */
    private static byte[] send(String strUrl, String reqMethod, byte[] reqData) {
        try {
            URL url = new URL(strUrl);
            HttpURLConnection httpcon = (HttpURLConnection) url.openConnection();
            httpcon.setDoOutput(true);
            httpcon.setDoInput(true);
            httpcon.setUseCaches(false);
            httpcon.setInstanceFollowRedirects(true);
            httpcon.setConnectTimeout(CONN_TIMEOUT);
            httpcon.setReadTimeout(READ_TIMEOUT);
            httpcon.setRequestMethod(reqMethod);
            httpcon.connect();
            if (reqMethod.equalsIgnoreCase(POST)) {
                OutputStream os = httpcon.getOutputStream();
                os.write(reqData);
                os.flush();
                os.close();
            }
            BufferedReader in = new BufferedReader(new InputStreamReader(httpcon.getInputStream(), "utf-8"));
            String inputLine;
            StringBuilder bankXmlBuffer = new StringBuilder();
            while ((inputLine = in.readLine()) != null) {
                bankXmlBuffer.append(inputLine);
            }
            in.close();
            httpcon.disconnect();
            return bankXmlBuffer.toString().getBytes();
        } catch (Exception ex) {
            log.error(ex.toString(), ex);
            return null;
        }
    }


    /**
     * ===================================================== HttpUtilClients ===============================================
     * ==============================================================================================================
     */
    public String postParamBody(String url, String jsonParam, Map<String, String> headers) {
        try {
            return postBodyJson(url, jsonParam, headers, "application/json", charset, connTimeout, readTimeout);
        } catch (Exception e) {
            log.info("[**{}**]", e.getMessage());
        } finally {
        }
        return "NON";
    }


    /**
     * @param requestWay 请求方式POST PUT DELETE GET
     * @param url        请求地址
     * @param bodyParams JSON对象参数
     * @param formParams Form表单参数
     * @param headers    头信息
     * @return
     */
    public String request(
            String requestWay, String url,
            String bodyParams, Map<String, String> formParams,
            Map<String, String> headers
    ) {
        return anyRequest(requestWay, url, bodyParams, formParams, headers,
                "application/json",
                charset,
                connTimeout,
                readTimeout
        );
    }


    /**
     * 注意连接池自动关闭的问题: 导致无法请求
     *
     * @param requestWay  请求方式POST PUT DELETE GET
     * @param url         请求地址
     * @param bodyParams  JSON对象参数
     * @param formParams  Form表单参数
     * @param headers     头信息
     * @param mimeType    媒体类型
     * @param charset     字符集
     * @param connTimeout 连接超时
     * @param readTimeout 读取超时
     * @return
     */
    private String anyRequest(
            String requestWay, String url,
            String bodyParams, Map<String, String> formParams,
            Map<String, String> headers,
            String mimeType, String charset, Integer connTimeout, Integer readTimeout
    ) {
        AbstractHttpMessage message = null;
        HttpEntityEnclosingRequestBase requestEntityBase = null;
        HttpRequestBase requestBase = null;
        CloseableHttpResponse closeableRes = null;
        CloseableHttpClient closeableHttpClient = null;
        String result = HttpConstant.RESULT_NON;
        //具体哪一种请求?
        switch (requestWay.toUpperCase()) {
            case "GET":
                requestBase = new HttpGet(url);
                message = requestBase;
                break;
            case "POST":
                requestEntityBase = new HttpPost(url);
                message = requestEntityBase;
                break;
            case "PUT":
                requestEntityBase = new HttpPut(url);
                message = requestEntityBase;
                break;
            case "DELETE":
                requestBase = new HttpDelete(url);
                message = requestBase;
                break;
            default:
                return HttpConstant.REQUEST_WAY_NON;
        }
        // headers 头信息处理
        message.setHeader("User-Agent", userAgentList.get(new Random().nextInt(userAgentList.size())));
        message.setHeader("Content-Type", "application/json");
        if (AssertNP.legalParams(headers)) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                message.setHeader(entry.getKey(), entry.getValue());
            }
        }
        try {
            //bodyParams, fromParams处理   post, put才有
            if (AssertNP.atLeastOneTrue(AssertNP.legalParams(bodyParams), AssertNP.legalParams(formParams))) {
                if (AssertNP.legalParams(bodyParams, requestEntityBase)) {
                    HttpEntity entity = new StringEntity(bodyParams, ContentType.create(mimeType, charset));
                    requestEntityBase.setEntity(entity);
                }
                if (AssertNP.legalParams(formParams, requestEntityBase)) {
                    List<NameValuePair> formParamList = new ArrayList<NameValuePair>();
                    for (Map.Entry<String, String> entry : formParams.entrySet()) {
                        formParamList.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
                    }
                    HttpEntity httpEntity = new UrlEncodedFormEntity(formParamList, "UTF-8");
                    requestEntityBase.setEntity(httpEntity);
                }
            }
            //请求配置处理
            if (AssertNP.atLeastOneTrue(null != requestBase, null != requestEntityBase)) {
                if (AssertNP.legalParams(requestBase)) {
                    requestBase.setConfig(this.getConfig());
                }
                if (AssertNP.legalParams(requestEntityBase)) {
                    requestEntityBase.setConfig(this.getConfig());
                }
            }
            //http? https? 处理
            if (url.startsWith("https")) {
                // 执行 Https 请求.
                closeableHttpClient = createSSLInsecureClient();
                if (AssertNP.legalParams(requestBase)) {
                    closeableRes = closeableHttpClient.execute(requestBase);
                } else {
                    closeableRes = closeableHttpClient.execute(requestEntityBase);
                }
                //处理响应结果
                result = IOUtils.toString(closeableRes.getEntity().getContent(), charset);
            } else {
                HttpClient client = null;
                HttpResponse httpResponse = null;
                // 执行 Http 请求.
                // client = HttpClients.custom().setConnectionManager(this.cm).build();
                client = HttpUtils.client;
                if (AssertNP.legalParams(requestBase)) {
                    httpResponse = client.execute(requestBase);
                } else {
                    httpResponse = client.execute(requestEntityBase);
                }
                //处理响应结果
                result = IOUtils.toString(httpResponse.getEntity().getContent(), charset);
            }
        } catch (Exception e) {
            log.info("[**======> {} **]", e.getMessage() + " >> " + e.getStackTrace()[0] + " >> " + e.getCause());
        } finally {
            ResourcesCloseUtils.close(requestBase, requestEntityBase);
            ResourcesCloseUtils.close(closeableRes);
            if (AssertNP.allTrue(url.startsWith("https"), null != closeableHttpClient, closeableHttpClient instanceof CloseableHttpClient)) {
                ResourcesCloseUtils.close(closeableHttpClient);
            }
        }
        return result;
    }


    /**
     * TODO 重新封装方法: url, 参数方式, 请求方式, 头信息, mimeType, 附加信息...charset  connTimeout  readTimeout
     *
     * @param url
     * @param params
     * @param headers
     * @return
     */
    public String postParamForm(String url, Map<String, String> params, Map<String, String> headers) {
        try {
            return postFormMap(url, params, headers, connTimeout, readTimeout);
        } catch (Exception e) {
            log.info("[**{}**]", e.getMessage());
        } finally {
        }
        return "NON";
    }

    /**
     * 发送一个 Post 请求, 使用指定的字符集编码.
     * HttpEntityEnclosingRequestBase
     *
     * @param url
     * @param body        RequestBody
     * @param mimeType    例如 application/json  application/xml "application/x-www-form-urlencoded" a=1&b=2&c=3
     * @param charset     编码
     * @param connTimeout 建立链接超时时间,毫秒.
     * @param readTimeout 响应超时时间,毫秒.
     * @return ResponseBody, 使用指定的字符集编码.
     * @throws ConnectTimeoutException 建立链接超时异常
     * @throws SocketTimeoutException  响应超时
     * @throws Exception
     */
    private String postBodyJson(String url, String body, Map<String, String> headers, String mimeType, String charset, Integer connTimeout, Integer readTimeout) throws IOException {
        HttpClient client = null;
        HttpPost post = new HttpPost(url);
        post.setHeader("User-Agent", userAgentList.get(new Random().nextInt(userAgentList.size())));
        post.setHeader("Content-Type", "application/json");
        if (AssertNP.legalParams(headers)) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                post.setHeader(entry.getKey(), entry.getValue());
            }
        }
        String result = "";
        HttpResponse res = null;
        try {
            if (StringUtils.isNotBlank(body)) {
                HttpEntity entity = new StringEntity(body, ContentType.create(mimeType, charset));
                post.setEntity(entity);
            }
            post.setConfig(this.getConfig());
            if (url.startsWith("https")) {
                // 执行 Https 请求.
                client = createSSLInsecureClient();
                res = client.execute(post);
            } else {
                // 执行 Http 请求.
                client = HttpClients.custom().setConnectionManager(this.cm).build();
                res = client.execute(post);
            }
            result = IOUtils.toString(res.getEntity().getContent(), charset);
        } catch (Exception e) {

        } finally {
            post.releaseConnection();
            if (url.startsWith("https") && client != null && client instanceof CloseableHttpClient) {
                ResourcesCloseUtils.close((AutoCloseable) res);
                ((CloseableHttpClient) client).close();
            }
        }
        return result;
    }


    /**
     * 提交form表单
     *
     * @param url
     * @param params
     * @param connTimeout
     * @param readTimeout
     * @return
     * @throws ConnectTimeoutException
     * @throws SocketTimeoutException
     * @throws Exception
     */
    private String postFormMap(String url, Map<String, String> params, Map<String, String> headers, Integer connTimeout, Integer readTimeout) throws ConnectTimeoutException,
            SocketTimeoutException, Exception {

        HttpClient client = null;
        HttpPost post = new HttpPost(url);
        post.setHeader("User-Agent", userAgentList.get(new Random().nextInt(userAgentList.size())));
        post.setHeader("Content-Type", "application/json");
        try {
            if (params != null && !params.isEmpty()) {
                List<NameValuePair> formParams = new ArrayList<NameValuePair>();
                Set<Map.Entry<String, String>> entrySet = params.entrySet();
                for (Map.Entry<String, String> entry : entrySet) {
                    formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
                }
                HttpEntity entity = new UrlEncodedFormEntity(formParams, "UTF-8");
                post.setEntity(entity);
            }
            if (headers != null && !headers.isEmpty()) {
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    post.setHeader(entry.getKey(), entry.getValue());
                }
            }
            post.setConfig(this.getConfig());
            HttpResponse res = null;
            if (url.startsWith("https")) {
                // 执行 Https 请求.
                client = createSSLInsecureClient();
                res = client.execute(post);
            } else {
                // 执行 Http 请求.
                client = HttpClients.custom().setConnectionManager(this.cm).build();
                res = client.execute(post);
            }
            return IOUtils.toString(res.getEntity().getContent(), "UTF-8");
        } finally {
            post.releaseConnection();
            if (url.startsWith("https") && client != null
                    && client instanceof CloseableHttpClient) {
                ((CloseableHttpClient) client).close();
            }
        }
    }


    /**
     * 不带头信息的get
     *
     * @param url
     * @return
     */
    public String get(String url) {
        try {
            return get(url, charset, connTimeout, readTimeout);
        } catch (Exception e) {
            log.info("[**{}**]", e.getMessage());
        } finally {
        }
        return "NON";
    }


    /**
     * 发送一个 GET 请求
     *
     * @param url
     * @param charset
     * @param connTimeout 建立链接超时时间,毫秒.
     * @param readTimeout 响应超时时间,毫秒.
     * @return
     * @throws ConnectTimeoutException 建立链接超时
     * @throws SocketTimeoutException  响应超时
     * @throws Exception
     */
    private String get(String url, String charset, Integer connTimeout, Integer readTimeout)
            throws ConnectTimeoutException, SocketTimeoutException, Exception {
        HttpClient client = null;
        HttpGet get = new HttpGet(url);
        get.setHeader("User-Agent", userAgentList.get(new Random().nextInt(userAgentList.size())));
        get.setHeader("Content-Type", "application/json");

        String result = "";
        try {
            get.setConfig(this.getConfig());
            HttpResponse res = null;
            if (url.startsWith("https")) {
                // 执行 Https 请求.
                client = createSSLInsecureClient();
                res = client.execute(get);
            } else {
                // 执行 Http 请求.
                client = HttpClients.custom().setConnectionManager(this.cm).build();
                res = client.execute(get);
            }
            result = IOUtils.toString(res.getEntity().getContent(), charset);
        } finally {
            get.releaseConnection();
            if (url.startsWith("https") && client != null && client instanceof CloseableHttpClient) {
                ((CloseableHttpClient) client).close();
            }
        }
        return result;
    }


    /**
     * 带头信息的get
     *
     * @param url
     * @param headers
     * @return
     */
    public String getH(String url, Map<String, String> headers) {
        try {
            return getWithHeader(url, headers, charset, connTimeout, readTimeout);
        } catch (Exception e) {
            log.info("[**{}**]", e.getMessage());
        } finally {
        }
        return "NON";
    }


    /**
     * setHeader(name, value)：如果Header中没有定义则添加，如果已定义则用新的value覆盖原用value值。
     * addHeader(name, value)：如果Header中没有定义则添加，如果已定义则保持原有value不改变。
     *
     * @param url
     * @param headers
     * @param charset
     * @param connTimeout
     * @param readTimeout
     * @return
     * @throws ConnectTimeoutException
     * @throws SocketTimeoutException
     * @throws Exception
     */
    private String getWithHeader(String url, Map<String, String> headers, String charset, Integer connTimeout, Integer readTimeout)
            throws ConnectTimeoutException, SocketTimeoutException, Exception {
        HttpClient client = null;
        HttpGet get = new HttpGet(url);
        get.setHeader("User-Agent", userAgentList.get(new Random().nextInt(userAgentList.size())));
        get.setHeader("Content-Type", "application/json");
        if (AssertNP.legalParams(headers)) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                get.setHeader(entry.getKey(), entry.getValue());
            }
        }

        String result = "";
        try {
            get.setConfig(this.getConfig());
            HttpResponse res = null;
            if (url.startsWith("https")) {
                // 执行 Https 请求.
                client = createSSLInsecureClient();
                res = client.execute(get);
            } else {
                // 执行 Http 请求.
                client = HttpClients.custom().setConnectionManager(this.cm).build();
                res = client.execute(get);
            }
            result = IOUtils.toString(res.getEntity().getContent(), charset);
        } finally {
            get.releaseConnection();
            if (url.startsWith("https") && client != null && client instanceof CloseableHttpClient) {
                ((CloseableHttpClient) client).close();
            }
        }
        return result;
    }

    /**
     * 从 response 里获取 charset
     *
     * @param ressponse
     * @return
     */
    @SuppressWarnings("unused")
    private static String getCharsetFromResponse(HttpResponse ressponse) {
        // Content-Type:text/html; charset=GBK
        if (ressponse.getEntity() != null && ressponse.getEntity().getContentType() != null && ressponse.getEntity().getContentType().getValue() != null) {
            String contentType = ressponse.getEntity().getContentType().getValue();
            if (contentType.contains("charset=")) {
                return contentType.substring(contentType.indexOf("charset=") + 8);
            }
        }
        return null;
    }


    /**
     * 创建 SSL连接
     *
     * @return
     * @throws GeneralSecurityException
     */
    private static CloseableHttpClient createSSLInsecureClient() throws GeneralSecurityException {
        try {
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                @Override
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true;
                }
            }).build();

            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new X509HostnameVerifier() {

                @Override
                public boolean verify(String arg0, SSLSession arg1) {
                    return true;
                }

                @Override
                public void verify(String host, SSLSocket ssl)
                        throws IOException {
                }

                @Override
                public void verify(String host, X509Certificate cert)
                        throws SSLException {
                }

                @Override
                public void verify(String host, String[] cns,
                                   String[] subjectAlts) throws SSLException {
                }
            });
            return HttpClients.custom().setSSLSocketFactory(sslsf).build();
        } catch (GeneralSecurityException e) {
            throw e;
        }
    }


}
